/* HTML5-Canvas-Presentation (create presentations with a clear structure) */
/*  Copyright (C) 2012  Michael Fürst */
/*  */
/*  This program is free software; you can redistribute it and/or modify it */
/*  under the terms of the GNU General Public License as published by the */
/*  Free Software Foundation; either version 3 of the License, or (at your */
/*  option) any later version. */
/*  */
/*  This program is distributed in the hope that it will be useful, but */
/*  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY */
/*  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License */
/*  for more details. */
/*  */
/*  You should have received a copy of the GNU General Public License along */
/*  with this program; if not, see <http:/* www.gnu.org/licenses/>. */


/* All keycodes, can also be used in a special data.js if required (editor would do so) */
var KEY_F1 = 112;
var KEY_F2 = 113;
var KEY_F3 = 114;
var KEY_F4 = 115;
var KEY_F5 = 116;
var KEY_F6 = 117;
var KEY_F7 = 118;
var KEY_F8 = 119;
var KEY_F9 = 120;
var KEY_F10 = 121;
var KEY_F11 = 122;
var KEY_F12 = 123;

var KEY_0 = 48;
var KEY_1 = 49;
var KEY_2 = 50;
var KEY_3 = 51;
var KEY_4 = 52;
var KEY_5 = 53;
var KEY_6 = 54;
var KEY_7 = 55;
var KEY_8 = 56;
var KEY_9 = 57;

var KEY_A = 65;
var KEY_B = 66;
var KEY_C = 67;
var KEY_D = 68;
var KEY_E = 69;
var KEY_F = 70;
var KEY_G = 71;
var KEY_H = 72;
var KEY_I = 73;
var KEY_J = 74;
var KEY_K = 75;
var KEY_L = 76;
var KEY_M = 77;
var KEY_N = 78;
var KEY_O = 89;
var KEY_P = 80;
var KEY_Q = 81;
var KEY_R = 82;
var KEY_S = 83;
var KEY_T = 84;
var KEY_U = 85;
var KEY_V = 86;
var KEY_W = 87;
var KEY_X = 88;
var KEY_Y = 89;
var KEY_Z = 90;

var KEY_LEFT = 37;
var KEY_RIGHT = 39;
var KEY_UP = 38;
var KEY_DOWN = 40;
var KEY_PAGE_UP = 33;
var KEY_PAGE_DOWN = 34;
var KEY_SPACE = 32;
var KEY_ENTER = 13;
var KEY_BACKSPACE = 8;
var KEY_ESCAPE = 27;
var KEY_SHIFT = 16;
var KEY_CTRL = 17;
var KEY_ALT = 18;
var KEY_TAB = 9;
var KEY_CAPSLOCK = 20;
var KEY_TILDE = 192;
var KEY_PAUSE = 19;
var KEY_PLUS = 187;
var KEY_MINUS = 189;
var KEY_NEG_ROTATION = 222;
var KEY_POS_ROTATION = 191;

var BUTTON_LEFT = 1;
var BUTTON_MIDDLE = 2;
var BUTTON_RIGHT = 3;


/* The presentation class */
/* this class is the core of every presentation and manages all you need */
var Presentation = (function() {
	var bottombar = true;
	var mousewheel = true;
	var max_fps = 120;
	/*  Extrem wichtig für die Animation des scrolls */
	var frame = 0.0;
	/*  Extrem wichtig für die Animation des scrolls */
	var step = 0;
	/*  Canvas-Element */
	var canvas = null;
	/*  2D-Kontext Canvas-Elements */
	var ctx = null;
	/* Default (not fullscreen canvas size */
	var default_width = 0;
	var default_height = 0;
	var fullscreen = false;
	/*  Current canvas size */
	var width = 0;
	var height = 0;
	/* mousemovement */
	var mouse = {isDown: false, pos: null};
	var last_cur_pos = {x: 0, y: 0};
	var dragged = false;
	var mouse_click_listener = null;
	var presstime = 0;
	/* display settings */
	var scalar;
	var angle;
	var dx;
	var dy;
	var partial;
	/* clipping area */
	var screen_radius = 0;
	/*  0 = presentation (path); 1 = presentation (free); */
	var mode = 0;
	/* path data */
	var data_steps = -1;
	var data_img = [];
	var data_img_loaded = [];
	var data_vid = [];
	var data_vid_running = [];
	var data_audio = [];
	var data_audio_running = [];
	var data_path_x = [];
	var data_path_y = [];
	var data_path_zoom = [];
	var data_path_rotation = [];
	var data_path_breakpoint = [];
	/* for calculating the real fps */
	var lastLoop = new Date().getTime();
	var fps = 0;
	/* time presentation is running (good to manage your time) */
	var start_time = new Date().getTime(); 
	/* for setting a custom background */
	var bkgGrad = null;
	var bkgDefault = false;
	/* keyhandler so you can define your own keyhandlers */
	var external_key_handler = null;
	var stopping = false;
	var debugmode = false;
	var keyboard_input_state = true;

	/* clear all local variables */
	function clear () {
		presstime = 0;
		bottombar = true;
		mousewheel = true;
		frame = 0;
		step = 0;
		canvas = null;
		ctx = null;
		default_width = 0;
		default_height = 0;
		fullscreen = false;
		width = 0;
		height = 0;
		mouse = {isDown: false, pos: null};
		last_cur_pos = {x: 0, y: 0};
		dragged = false;
		mouse_click_listener = null;
		screen_radius = 0;
		mode = 0;
		debugmode = false;
		data_steps = -1;
	        data_img = [];
		data_img_loaded = [];
	        data_vid = [];
		data_vid_running = [];
		data_audio = [];
		data_audio_running = [];
		data_path_x = [];
		data_path_y = [];
		data_path_zoom = [];
		data_path_rotation = [];
		data_path_breakpoint = [];
		lastLoop = new Date().getTime();
		fps = 0;
		start_time = new Date().getTime(); 
		bkgGrad = null;
		bkgDefault = false;
		external_key_handler = null;
		keyboard_input_state = true;
	}

	/* update cursor position (required for free mode) */
	function getCursorPosition(e) {
		var pos = {
			x: 0, y: 0}
		;
		if (e.pageX != undefined && e.pageY != undefined) {
			pos.x = e.pageX;
			pos.y = e.pageY;
		}
		else {
			pos.x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
			pos.y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
		}
		var canv_pos = getPosition(canvas);
		pos.x -= canv_pos.x;
		pos.y -= canv_pos.y;
		return pos;
	}
	function getPosition(element) {
  		var elem=element,tagname="",x=0,y=0;
  		while ((typeof(elem)=="object")&&(typeof(elem.tagName)!="undefined")) {
    			y+=elem.offsetTop;
    			x+=elem.offsetLeft;
    			tagname=elem.tagName.toUpperCase();
			if (tagname=="BODY")
      				elem=0;
			if (typeof(elem)=="object")
				if (typeof(elem.offsetParent)=="object")
					elem=elem.offsetParent;
		}
  		position=new Object();
  		position.x=x;
  		position.y=y;
  		return position;
	}
/***************** 
* Input Handlers * 
*****************/
	/* helper function */
	function attachEvent(obj, event, callback) {
		/*  Fallunterscheidung automatisch, damit man es nicht immer manuell machen muss */
		if (obj.addEventListener) obj.addEventListener(event, callback, false);
		else if (obj.attachEvent) obj.attachEvent('on' + event, callback);
		else obj['on' + event] = callback;
	}
	/* registers mousdown event */
	function onmousedown(e) {
		if (e.which == BUTTON_LEFT) {
			dragged = false;
			var pos = getCursorPosition(e);
			mouse.isDown = true;
			mouse.pos = pos;
			presstime = new Date().getTime();
		}
		if (e.which == BUTTON_MIDDLE) {
			dragged = false;
			var pos = getCursorPosition(e);
			mouse.mid = true;
			mouse.pos = pos;
		}
	}
	/* if mouse is not dragged call mouseclick listener */
	function onmouseup(e) {
		if (e.which == BUTTON_LEFT) {
			mode = 1;
			mouse.isDown = false;
			if (dragged != true && typeof (mouse_click_listener) == 'function') {
				presstime = (new Date().getTime())-presstime;
				var dx2 = Math.cos(-angle*Math.PI/180)*((mouse.pos.x-width/2)/scalar)-Math.sin(-angle*Math.PI/180)*((mouse.pos.y-height/2)/scalar);
				var dy2 = Math.sin(-angle*Math.PI/180)*((mouse.pos.x-width/2)/scalar)+Math.cos(-angle*Math.PI/180)*((mouse.pos.y-height/2)/scalar);
				mouse_click_listener(-dx/scalar+dx2, -dy/scalar+dy2, angle, scalar, partial, presstime);
			} else if (dragged == false) {
				next();
			}
			presstime = 0;
		}
		if (e.which == BUTTON_MIDDLE) {
			mode = 1;
			mouse.mid = false;
			if (dragged == false && mouse_click_listener != null) {
				
			}
		}
	}
	/* Here the free movement is implemented (on mouse drag) */
	function onmousemove(e) {
		var pos = getCursorPosition(e);
		mouse.pos = pos;
		if (mouse.isDown == true) {
			dragged = true;
			mode = 1;
			dx += Math.cos(angle*Math.PI/180)*(pos.x -last_cur_pos.x)+Math.sin(angle*Math.PI/180)*(pos.y -last_cur_pos.y);
			dy += -Math.sin(angle*Math.PI/180)*(pos.x -last_cur_pos.x)+Math.cos(angle*Math.PI/180)*(pos.y -last_cur_pos.y);
			presstime = new Date().getTime();
		}
		if (mouse.mid == true) {
			dragged = true;
			mode = 1;
			angle += (pos.x-last_cur_pos.x)/width*360;
		}
		last_cur_pos = pos;
	}
	/* keyinput handler */
	function onkeydown(e) {
		if (keyboard_input_state == true) {
			var key = e.keyCode;
			switch(key) {
			case KEY_F : toggle_fullscreen(); break;
			case KEY_F11 : force_fullscreen(true); break;
			case KEY_ESCAPE : mode = 0; step = 0; frame = 0; force_fullscreen(false); break;
			case KEY_RIGHT : next(); break;
			case KEY_PAGE_DOWN : next (); break;
			case KEY_SPACE : next(); break;
			case KEY_LEFT : prev(); break;
			case KEY_PAGE_UP : prev (); break;
			case KEY_PLUS : break; // only usable in chrome :/
			case KEY_MINUS : break; // only usable in chrome :/
			case KEY_NEG_ROTATION : mode = 1; angle--; break;
			case KEY_POS_ROTATION : mode = 1; angle++; break;
			/* browser reserved */
			case KEY_F5 : break;
			case KEY_BACKSPACE : break;
			/* pass all that was not caught to custom handler */
			default: if (external_key_handler != null) external_key_handler(key, angle, scalar, partial); break;
			}
		}
	}
	function onwheel(event){
		if (mousewheel == false) return;
		var delta = 0;
		if (!event) event = window.event;
		if (event.wheelDelta) {
			delta = event.wheelDelta/120;
		} else if (event.detail) { delta = -event.detail/3;	}
		var final_delta =  Math.round(delta); //Safari Round
		if (final_delta > 0) {
			plus();
		} else if (final_delta < 0) {
			minus();
		}
    	}

	/* switch fullscreen flag & change pos attr for correct positioning */
	function toggle_fullscreen() {
		if (fullscreen) {
			canvas.style.position = "relative";
			fullscreen = false;
		} else {
			canvas.style.position = "absolute";
			fullscreen = true;
		}
	}
	/* force a certain fullscreen state */
	function force_fullscreen(b_full) {
		if (b_full == true) {
			canvas.style.position = "absolute";
			fullscreen = true;
		} else {
			canvas.style.position = "relative";
			fullscreen = false;
		}
	}
	/* go to next pathpos */
	function next() {
		mode = 0;
		frame = data_path_breakpoint[step]+1;
		step += 1;
		if (step > data_steps) {
			step = 0;
			frame = 0;
		}
	}
	/* go to last pathpos */
	function prev() {
		mode = 0;
		step -= 1;
		if (step < 0) {
			step = data_steps;
		}
		frame = data_path_breakpoint[step]-1;
	}
	/* zoom in */
	function plus () {
		mode = 1;
		scalar *= 1.1; 
		dy *= 1.1;
		dx *= 1.1;
	}
	/* zoom out */
	function minus() {
		mode = 1;
		scalar /= 1.1;
		dy /= 1.1;
		dx /= 1.1;
	}
/******************************* 
* All related to the rendering * 
*******************************/
	/* gives you back the time the presentation is currently running, resets on reload */
	function getPresentationTime() {
		var time = (((new Date().getTime())-start_time)/1000);
		var time_str = "";
		var time_h = Math.floor((time/3600));
		time = time -(time_h*3600);
		var time_m = Math.floor((time/60));
		time = time -(time_m*60);
		if (time_h < 10) {
			time_str = "0"+time_h;
		} else {
			time_str += ""+time_h;
		}
		if (time_m < 10) {
			time_str += ":0"+time_m;
		} else {
			time_str += ":"+time_m;
		}
		if (Math.floor(time) < 10) {
			time_str += ":0"+Math.floor(time);
		} else {
			time_str += ":"+Math.floor(time);
		}
		return time_str;
	}
	/* helper function to interpolate from a to b */
	function interpolate(x, b, c) {
		return b+(x/100*(c-b));
	}
	/* prints out the percentage of the current step */
	function getStepPercent() {
		if (step == 0) {
			return frame*100/data_path_breakpoint[step];
		}
		return (frame-data_path_breakpoint[step-1])*100/(data_path_breakpoint[step]-data_path_breakpoint[step-1]);
	}
	/* calculate dx */
	function getDX(partial) {
		if (step == 0) {
			return interpolate(getStepPercent(), 0, -data_path_x[step])*partial;
		}
		return interpolate(getStepPercent(), -data_path_x[step-1], -data_path_x[step])*partial;
	}
	/* calculate dy */
	function getDY(partial) {
		if (step == 0) {
			return interpolate(getStepPercent(), 0, data_path_y[step]*partial);
		}
		return interpolate(getStepPercent(), -data_path_y[step-1], -data_path_y[step])*partial;
	}
	/* calculate the current zoom lvl */
	function getZoom() {
		if (step == 0) {
			return interpolate(getStepPercent(), 1, data_path_zoom[step]);
		}
		return interpolate(getStepPercent(), data_path_zoom[step-1], data_path_zoom[step]);
	}
	
	/* calculate current rotation */
	function getRotation() {
		if (step == 0) {
			return interpolate(getStepPercent(), 0, data_path_rotation[step]);
		}
		return interpolate(getStepPercent(), data_path_rotation[step-1], data_path_rotation[step]);
	}
	
	/* call data_render, draw background and everything ontop of it */
	function render() {
		/* clean canvas */
		ctx.clearRect(0, 0, width, height);
		/*  Canvas-Hintergrund zeichnen */
		if (bkgGrad == null || bkgDefault == true) {
			bkgGrad = ctx.createLinearGradient(0, 0, 0, height);
			bkgGrad.addColorStop(0.5, '#000022');
			bkgGrad.addColorStop(0.8, '#000015');
			bkgGrad.addColorStop(0.9, '#000006');
			bkgGrad.addColorStop(1, '#000000');
			ctx.fillStyle = bkgGrad;
			ctx.fillRect(0, 0, width, height);
			bkgDefault = true;
		}
		/*  if  there exists a presentation draw it */
		if (data_steps > -1) {
			if (step == 0) {
				data_background(step, interpolate(frame*100/data_path_breakpoint[step], 0, 1));
			} else {
				data_background(step, interpolate((frame-data_path_breakpoint[step-1])*100/(data_path_breakpoint[step]-data_path_breakpoint[step-1]), 0, 1));
			}
			/*  Normalize proportions to screen size of data_width, data_height */
			partial = data_width/width;
			if (partial < data_height/height) {
				partial = data_height/height;
			}
			if (mode == 0) {
				scalar = getZoom()/partial;
				angle = getRotation();
				dx = getDX(scalar);
				dy = getDY(scalar);
			}

			/*  Translate and rotate bg so that (0|0) would be center with dx=dy=0 */
			ctx.translate(width/2, height/2);
			ctx.rotate(angle*Math.PI /180);
			ctx.translate(dx, dy);

			/* set default color, to enable drawing if user does not use color function */
			Presentation.colorRGB(255,255,255);

			/*  Render data */
			data_render();

			/*  Get back to normal setting to draw some text */
			ctx.translate(-dx, -dy);
			ctx.rotate((-angle)*Math.PI /180);
			ctx.translate(-width/2, -height/2);

			if (presstime > 0) {
				var delta_press = (new Date().getTime()-presstime);
				if (delta_press > 2000) {
					ctx.fillStyle = "rgba(0,255,0,0.3)";
					ctx.beginPath();
					ctx.arc(mouse.pos.x, mouse.pos.y, 20, 0, 360, false);
					ctx.fill();
					dragged = false;
				} else if (delta_press > 1000) {
					ctx.fillStyle = "rgba(255,255,0,0.3)";
					ctx.beginPath();
					ctx.arc(mouse.pos.x, mouse.pos.y, 20, 0, 360, false);
					ctx.fill();
					dragged = false;
				} else if (delta_press > 300) {
					ctx.fillStyle = "rgba(255,0,0,0.3)";
					ctx.beginPath();
					ctx.arc(mouse.pos.x, mouse.pos.y, 20, 0, 360, false);
					ctx.fill();
				}
			}

			Presentation.colorRGBA(0,0, 0, 0.9);
			if (width > data_width/partial) {
				ctx.fillRect(0,0,(width-data_width/partial)/2, height);
				ctx.fillRect(width-(width-data_width/partial)/2,0,(width-data_width/partial)/2, height);
			}
			if (height > data_height/partial) {
				ctx.fillRect(0,0,width,(height-data_height/partial)/2);
				ctx.fillRect(0, height-(height-data_height/partial)/2,width,(height-data_height/partial)/2);
			}

			/*  Bottom pane */
			if(bottombar == true) {		
				ctx.font = "12px 'Helvetica'";
				ctx.fillStyle = "rgb(128,128,128)";
				ctx.textAlign = 'center';
				ctx.textBaseline = 'top';
				ctx.font = "12px 'Helvetica'";
				ctx.fillText("Time: "+getPresentationTime(), 50, height-15);
				ctx.font = "20px 'Helvetica'";
				if (data_steps < 10) {
					ctx.fillText("("+step+"/"+data_steps+")", width-30, height-22);
				}
				else if (data_steps < 100) {
					if (step < 10) {
						ctx.fillText("(0"+step+"/"+data_steps+")", width-40, height-22);
					}
					else {
						ctx.fillText("("+step+"/"+data_steps+")", width-40, height-22);
					}
				}
				else {
					if (step < 10) {
						ctx.fillText("(00"+step+"/"+data_steps+")", width-50, height-22);
					}
					else if (step < 100) {
						ctx.fillText("(0"+step+"/"+data_steps+")", width-50, height-22);
					}
					else {
						ctx.fillText("("+step+"/"+data_steps+")", width-50, height-22);
					}
				}
			}
		}
		else {
			ctx.font = "44px 'Helvetica'";
			ctx.fillStyle = "rgb(255,255,255)";
			ctx.textAlign = 'center';
			ctx.textBaseline = 'top';
			ctx.fillText("Could not find presentation!", width/2, height/2);
		}

		if (bottombar == true) {
			/*  Inventor notice */
			ctx.font = "12px 'Helvetica'";
			ctx.fillStyle = "rgb(128,128,128)";
			ctx.textAlign = 'center';
			ctx.textBaseline = 'top';
			ctx.fillText("Invented by Michael Fürst 2012", width/2, height-15);
		}
	}
	/* this manages the frame */
	function move() {
		if (stopping == true) {
			return;
		}
		Presentation.onchange();
		var thisLoop = new Date().getTime(); 
    		fps = 1000.0/(thisLoop - lastLoop);
		lastLoop = new Date().getTime();
		if (data_steps > -1) {
			frame += fps/60.0;
			if (frame > data_path_breakpoint[step]) {
				frame = data_path_breakpoint[step]}
		}
		if (debugmode == true) {
			ctx.font = "12px 'Helvetica'";
			ctx.textAlign = 'center';
			ctx.textBaseline = 'top';
			if (fps > 60) {
				ctx.fillStyle = "rgb(0,255,255)";
			} else if (fps > 30) {
				ctx.fillStyle = "rgb(0,255,0)";
			} else if (fps > 15) {
				ctx.fillStyle = "rgb(255,255,0)";
			} else {
				ctx.fillStyle = "rgb(255,0,0)";
			}
			ctx.fillText("FPS: "+fps.toFixed(0), 40, 15);
		}
		window.setTimeout(move, 0);
		//window.requestNextAnimationFrame(move);
	}
	/* helper to draw a line from a to b */
	function drawLine(intMoveX, intMoveY, intDestX, intDestY){
		/*  Neuen Arbeitspfad anlegen */
		ctx.beginPath();
		ctx.moveTo(intMoveX, intMoveY);
		ctx.lineTo(intDestX, intDestY);
		ctx.stroke();
	}
	/* helper to draw an arc */
	function drawArc(center_x, center_y, radius, begin, end, anti_clockwise, filled) {
		ctx.moveTo(center_x, center_y);
		ctx.beginPath();
		ctx.arc(center_x, center_y, radius, begin, end, anti_clockwise);
		if (filled == true) {
			ctx.fill();
		} else {
			ctx.stroke();
		}
	}
	/* improves the performance dependant on the system/browser */
	window.requestNextAnimationFrame = (function () {
		return window.requestAnimationFrame || window.webkitRequestAnimationFrame
		|| window.mozRequestAnimationFrame  || window.oRequestAnimationFrame
		 || window.msRequestAnimationFrame ||
		function (callback, element) {
			var self = this, start, finish;
			window.setTimeout( function() {
				start = +new Date();
				callback(start);
				finish = +new Date();
				self.timeout = 1000 / max_fps - (finish - start);
			}, self.timeout);
		};
	})();
	/* don't draw things that are not visible to improve the performance */
	function clipping (x, y, size) {
		if (lengthOf(dx/scalar+x, dy/scalar+y) < size+(screen_radius/scalar)) {
			return true;
		}
		return false;
	}
	/* helper to calc length of a vect */
	function lengthOf(a, b) {
		return Math.sqrt((a*a)+(b*b));
	}
/******************* 
* Public functions * 
*******************/
	return {
		/* deactivate mouswheel to zoom, can be annoying on some pages */
		setMouswheel: function(b_active) {
			mousewheel = b_active;
		},
		/* force a certain fullscreen state */
		setScreenmode: function(b_full) {
			if (b_full == true) {
				canvas.style.position = "relative";
				fullscreen = true;
			} else {
				canvas.style.position = "relative";
				fullscreen = false;
			}
		},
		setResolution: function(s_width, s_height) {
			default_width = s_width;
			default_height = s_height;
			Presentation.onchange();
		},
		setBottompane: function(b_active) {
			bottombar = b_active;
		},
		clearPath: function() {
			data_path_x = [];
			data_path_y = [];
			data_path_zoom = [];
			data_path_rotation = [];
			data_path_breakpoint = [];
		},
		setPathpos: function(pos) {
			step = pos;
			if (step > data_path_breakpoint.length) {
				step = data_path_breakpoint.length;
			}
			frame = data_path_breakpoint[step];
		},
		setKeyboardInputState: function (b_active) {
			keyboard_input_state = b_active;
		},
		getZoom: function() {
			return scalar;
		},
		getX: function() {
			return dx;
		},
		getY: function() {
			return dy;
		},
		getAngle: function() {
			return angle;
		},
		/* invoke the presentation */
		start: function(s_full, s_width, s_height, obj) {
			stopping = true;
			clear();
			default_width = s_width;
			default_height = s_height;
			fullscreen = s_full;
			canvas = document.createElement('canvas');
  			canvas.setAttribute('id','html5-canvas-presentation-canv');
			if (obj == null) {
  				document.body.appendChild(canvas);
			} else {
  				document.getElementById(obj).appendChild(canvas);
			}
			if (fullscreen) {
				canvas.style.position = "absolute";
			} else {
				canvas.style.position = "relative";
			}
			canvas.style.top = "0";
			canvas.style.left = "0";
			canvas.style.display = 'block';
			attachEvent(canvas, 'mousedown', onmousedown);
			attachEvent(canvas, 'mouseup', onmouseup);
			attachEvent(canvas, 'mousemove', onmousemove);
            		attachEvent(window, 'DOMMouseScroll', onwheel);
            		attachEvent(window, 'mousewheel', onwheel);
            		attachEvent(document, 'mousewheel', onwheel);
            		attachEvent(window, 'scroll', onwheel);
            		attachEvent(document, 'scroll', onwheel);
			window.onkeydown = onkeydown;
			ctx = canvas.getContext('2d');
			ctx.save();
			Presentation.onchange();
			data_steps = -1;
			data_init();
			/* the heartbeat */
			stopping = false;
			window.requestNextAnimationFrame(move);
		},
		/*  Wenn mal die Fenstergröße verändert wird... */
		onchange: function() {
			if (fullscreen == true) {
				width = window.innerWidth;
				height = window.innerHeight;
			} else {
				width = default_width;
				height = default_height;
			}
			canvas.width = width;
			canvas.height = height;
			screen_radius = lengthOf(width, height)/2;
			render();
		},
		isRunning: function() {
			if (canvas != null) {
				return true;
			} else {
				return false;
			}
		},
		/* eigentlich nicht notwendig */
		stop: function() {
			stopping = true;
		},
		/* make width public */
		getWidth: function() {
			return width;
		},
		/* make height public */
		getHeight: function() {
			return height;
		},
/****** 
* API * 
******/
		/* set your custom keyhandler */
		setKeyhandler: function (keyhandler) {
			if (typeof keyhandler == 'function') {
				external_key_handler = keyhandler;
			}
		},
		/* set your custom mousehandler */
		setMousehandler: function (mousehandler) {
			if (typeof mousehandler == 'function') {
				mouse_click_listener = mousehandler;
			}
		},
		/* append a pathpoint */
		appendPath: function(x, y, zoom, rotation) {
			Presentation.appendPathE(x, y, zoom, rotation, 600);
		},
		/* append a pathpoint with time to reach it in ms */
		appendPathE: function(x, y, zoom, rotation, breakpoint) {
			var insertion_position = data_path_breakpoint.length;
			if (insertion_position > 0) {
				data_path_breakpoint[insertion_position] = (data_path_breakpoint[insertion_position-1])+breakpoint*6/100;
			} else {
				data_path_breakpoint[insertion_position] = breakpoint*6/100;
			}
			data_path_x[insertion_position] = x;
			data_path_y[insertion_position] = y;
			data_path_zoom[insertion_position] = zoom;
			data_path_rotation[insertion_position] = rotation;
			data_steps = data_path_breakpoint.length-1;
		},


		/* animate text in between the given steps (repeats animation after 1 step) */
		textA: function(text_x, text_y, new_x, new_y, text_str, text_size, new_size, text_angle, new_angle, text_begin, text_end) {
			if (step >= text_begin && step <= text_end) {
				Presentation.text(interpolate(getStepPercent(), text_x, new_x), interpolate(getStepPercent(), text_y, new_y), text_str, interpolate(getStepPercent(), text_size, new_size), interpolate(getStepPercent(), text_angle, new_angle));
			} else if (step < text_begin) {
				Presentation.text(text_x, text_y, text_str, text_size, text_angle);
			} else {
				Presentation.text(new_x, new_y, text_str, new_size, new_angle);
			}
		},
		/* animate text inbetween the given steps (only existing while animated, animation repeats after 1 step) */
		textAE: function(text_x, text_y, new_x, new_y, text_str, text_size, new_size, text_angle, new_angle, text_begin, text_end) {
			if (step >= text_begin && step <= text_end) {
				Presentation.text(interpolate(getStepPercent(), text_x, new_x), interpolate(getStepPercent(), text_y, new_y), text_str, interpolate(getStepPercent(), text_size, new_size), interpolate(getStepPercent(), text_angle, new_angle));
			}
		},
		/* animated box */
		boxA: function(box_x, box_y, new_x, new_y, box_width, box_height, new_width, new_height, b_round, box_angle, new_angle, begin, end, filled) {
			if (step >= begin && step <= end) {
				Presentation.box(interpolate(getStepPercent(), box_x, new_x), interpolate(getStepPercent(), box_y, new_y), interpolate(getStepPercent(), box_width, new_width), interpolate(getStepPercent(), box_height, new_height), b_round,  interpolate(getStepPercent(), box_angle, new_angle), filled);
			} else if (step < begin) {
				Presentation.box(box_x, box_y, box_width, box_height, b_round, box_angle, filled);
			} else {
				Presentation.box(new_x, new_y, new_width, new_height, b_round, new_angle, filled);
			}
		},
		/* animated box */
		boxAE: function(box_x, box_y, new_x, new_y, box_width, box_height, new_width, new_height, b_round, box_angle, new_angle, begin, end, filled) {
			if (step >= begin && step <= end) {
				Presentation.box(interpolate(getStepPercent(), box_x, new_x), interpolate(getStepPercent(), box_y, new_y), interpolate(getStepPercent(), box_width, new_width), interpolate(getStepPercent(), box_height, new_height), b_round,  interpolate(getStepPercent(), box_angle, new_angle), filled);
			}
		},
		/* animated circle */
		circleA: function(circle_x, circle_y, new_x, new_y, circle_radius, new_radius, begin, end, filled) {
			if (step >= begin && step <= end) {
				Presentation.circle(interpolate(getStepPercent(), circle_x, new_x), interpolate(getStepPercent(), circle_y, new_y), interpolate(getStepPercent(), circle_radius, new_radius), filled);
			} else if (step < begin) {
				Presentation.circle(circle_x, circle_y, circle_radius, filled);
			} else {
				Presentation.circle(new_x, new_y, new_radius, filled);
			}
		},
		/* animated circle */
		circleAE: function(circle_x, circle_y, new_x, new_y, circle_radius, new_radius, begin, end, filled) {
			if (step >= begin && step <= end) {
				Presentation.circle(interpolate(getStepPercent(), circle_x, new_x), interpolate(getStepPercent(), circle_y, new_y), interpolate(getStepPercent(), circle_radius, new_radius), filled);
			}
		},
		/* animated line */
		lineA: function(line_x, line_y, new_x, new_y, line_x2, line_y2, new_x2, new_y2, begin, end) {
			if (step >= begin && step <= end) {
				Presentation.line(interpolate(getStepPercent(), line_x, new_x), interpolate(getStepPercent(), line_y, new_y), interpolate(getStepPercent(), line_x2, new_x2), interpolate(getStepPercent(), line_x2, new_y2));
			} else if (step < begin) {
				Presentation.line(line_x, line_y, line_x2, line_y2);
			} else {
				Presentation.line(new_x, new_y, new_x2, new_y2);
			}
		},
		/* animated line */
		lineAE: function(line_x, line_y, new_x, new_y, line_x2, line_y2, new_x2, new_y2, begin, end) {
			if (step >= begin && step <= end) {
				Presentation.line(interpolate(getStepPercent(), line_x, new_x), interpolate(getStepPercent(), line_y, new_y), interpolate(getStepPercent(), line_x2, new_x2), interpolate(getStepPercent(), line_x2, new_y2));
			}
		},
		/* draw a text */
		textE: function(text_x, text_y, text_str, text_size, text_angle, text_appear, text_disappear) {
			if (step >= text_appear && step <= text_disappear) {
				Presentation.text(text_x, text_y, text_str, text_size, text_angle);
			}
		},
		/* draw a line */
		lineE: function(line_x, line_y, line_end_x, line_end_y, line_appear, line_disappear) {
			if (step >= line_appear && step <= line_disappear) {
				Presentation.line(line_x, line_y, line_end_x, line_end_y);
			}
		},
		/* draw  a circle */
		circleE: function(circle_x, circle_y, circle_radius, circle_appear, circle_disappear, filled) {
			if (step >= circle_appear && step <= circle_disappear) {
				Presentation.circle(circle_x, circle_y, circle_radius, filled);
			}
		},
		/* draw a box */
		boxE: function(box_x, box_y, box_width, box_height, box_b_round, box_angle, box_appear, box_disappear, filled) {
			if (step >= box_appear && step <= box_disappear) {
				Presentation.box(box_x, box_y, box_width, box_height, box_b_round, box_angle, filled);
			}
		},
		/* draw a permanent text */
		text: function(text_x, text_y, text_str, text_size, text_angle) {
			/* this is not nice, but it improves performance drastically */
			if (clipping(text_x, text_y, text_size/3*text_str.length) == true && (text_size*scalar) > 1/2) { 
				ctx.translate(text_x*scalar, (text_y)*scalar);
				ctx.rotate((text_angle)*Math.PI /180);
				ctx.translate(-text_x*scalar, -(text_y)*scalar);
				ctx.font = (text_size*scalar)+"px 'Helvetica'";
				ctx.textAlign = 'center';
				ctx.fillText(text_str, (text_x)*scalar, (text_y+text_size/3)*scalar);
				ctx.translate(text_x*scalar, (text_y)*scalar);
				ctx.rotate((-text_angle)*Math.PI /180);
				ctx.translate(-text_x*scalar, -(text_y)*scalar);
			}
		},
		/* draw a permanent line */
		line: function(line_x, line_y, line_end_x, line_end_y) {
			if (clipping((line_x+line_end_x)/2, (line_y+line_end_y)/2, lengthOf(line_end_x-line_x, line_end_y-line_y)/2) == true) {
				drawLine(line_x*scalar, line_y*scalar, line_end_x*scalar, line_end_y*scalar);
			}
		},
		/* draw a permanent circle */
		circle: function(circle_x, circle_y, circle_radius, filled) {
			if (clipping(circle_x, circle_y, circle_radius) == true) {
				drawArc(circle_x*scalar, circle_y*scalar, circle_radius*scalar, 0, Math.PI*2, true, filled); 
			}
		},
		/* draw a permanent box */
		box: function(box_x, box_y, box_width, box_height, box_b_round, box_angle, filled) {
			if (clipping(box_x, box_y, lengthOf(box_width, box_height)/2) == true) {
				box_y = box_y-(box_height/2);
				box_x = box_x-(box_width/2);
				ctx.translate((box_x+box_width/2)*scalar, (box_y+box_height/2)*scalar);
				ctx.rotate((box_angle)*Math.PI /180);
				ctx.translate(-(box_x+box_width/2)*scalar, -(box_y+box_height/2)*scalar);
				var xpos = box_x*scalar;
				var ypos = box_y*scalar;
				if (box_b_round) {
					ctx.strokeRect(xpos, ypos, box_width*scalar, box_height*scalar);
				} else {
					if (filled == true) {
						ctx.fillRect(xpos, ypos, box_width*scalar, box_height*scalar);
					} else {
						ctx.strokeRect(xpos, ypos, box_width*scalar, box_height*scalar);
					}
				}
				ctx.translate((box_x+box_width/2)*scalar, (box_y+box_height/2)*scalar);
				ctx.rotate((-box_angle)*Math.PI /180);
				ctx.translate(-(box_x+box_width/2)*scalar, -(box_y+box_height/2)*scalar);
			}
		},
		/* draw a permanent image */
		imgS: function(img_id, img_x, img_y, img_width, img_height, img_angle) {
			if (data_img_loaded[img_id] == true && clipping(img_x, img_y, lengthOf(img_width, img_height)/2) == true) {
				img_y = img_y-(img_height/2);
				img_x = img_x-(img_width/2);
				ctx.translate((img_x+img_width/2)*scalar, (img_y+img_height/2)*scalar);
				ctx.rotate((img_angle)*Math.PI /180);
				ctx.translate(-(img_x+img_width/2)*scalar, -(img_y+img_height/2)*scalar);
				var xpos = img_x*scalar;
				var ypos = img_y*scalar;
				/* draw the scaled image */
				ctx.drawImage(data_img[img_id],xpos, ypos, img_width*scalar, img_height*scalar);
				ctx.translate((img_x+img_width/2)*scalar, (img_y+img_height/2)*scalar);
				ctx.rotate((-img_angle)*Math.PI /180);
				ctx.translate(-(img_x+img_width/2)*scalar, -(img_y+img_height/2)*scalar);
			}
		},
		/* draw a permanent image */
		img: function(img_id, img_x, img_y, img_angle) {
			if (data_img_loaded[img_id] != true) {return;}
			var img_width = data_img[img_id].width;
			var img_height = data_img[img_id].height;
			if (data_img_loaded[img_id] == true && clipping(img_x, img_y, lengthOf(img_width, img_height)/2) == true) {
				img_y = img_y-(img_height/2);
				img_x = img_x-(img_width/2);
				ctx.translate((img_x+img_width/2)*scalar, (img_y+img_height/2)*scalar);
				ctx.rotate((img_angle)*Math.PI /180);
				ctx.translate(-(img_x+img_width/2)*scalar, -(img_y+img_height/2)*scalar);
				var xpos = img_x*scalar;
				var ypos = img_y*scalar;
				/* draw the scaled image */
				ctx.drawImage(data_img[img_id],xpos, ypos, img_width*scalar, img_height*scalar);
				ctx.translate((img_x+img_width/2)*scalar, (img_y+img_height/2)*scalar);
				ctx.rotate((-img_angle)*Math.PI /180);
				ctx.translate(-(img_x+img_width/2)*scalar, -(img_y+img_height/2)*scalar);
			}
		},
		/* draw a permanent image */
		imgP: function(img_id, img_x, img_y, img_scalar, img_angle) {
			if (data_img_loaded[img_id] != true) {return;}
			var img_width = data_img[img_id].width*img_scalar;
			var img_height = data_img[img_id].height*img_scalar;
			if (data_img_loaded[img_id] == true && clipping(img_x, img_y, lengthOf(img_width, img_height)/2) == true) {
				img_y = img_y-(img_height/2);
				img_x = img_x-(img_width/2);
				ctx.translate((img_x+img_width/2)*scalar, (img_y+img_height/2)*scalar);
				ctx.rotate((img_angle)*Math.PI /180);
				ctx.translate(-(img_x+img_width/2)*scalar, -(img_y+img_height/2)*scalar);
				var xpos = img_x*scalar;
				var ypos = img_y*scalar;
				/* draw the scaled image */
				ctx.drawImage(data_img[img_id],xpos, ypos, img_width*scalar, img_height*scalar);
				ctx.translate((img_x+img_width/2)*scalar, (img_y+img_height/2)*scalar);
				ctx.rotate((-img_angle)*Math.PI /180);
				ctx.translate(-(img_x+img_width/2)*scalar, -(img_y+img_height/2)*scalar);
			}
		},
		/* animated, perma img */
		imgA: function(img_id, img_x, img_y, new_x, new_y, img_width, img_height, new_width, new_height, img_angle, new_angle, begin, end) {
			if (step >= begin && step <= end) {
				Presentation.imgS(interpolate(getStepPercent(), box_x, new_x), interpolate(getStepPercent(), box_y, new_y), interpolate(imd_id, getStepPercent(), img_width, new_width), interpolate(getStepPercent(), img_height, new_height), interpolate(getStepPercent(), img_angle, new_angle));
			} else if (step < begin) {
				Presentation.imgS(img_id, img_x, img_y, img_width, img_height, img_angle);
			} else {
				Presentation.imgS(imd_id, new_x, new_y, new_width, new_height, new_angle);
			}
		},
		/* animated, perma img */
		imgPA: function(img_id, img_x, img_y, new_x, new_y, img_scalar, new_scalar, img_angle, new_angle, begin, end) {
			if (step >= begin && step <= end) {
				Presentation.imgP(img_id, interpolate(getStepPercent(), img_x, new_x), interpolate(getStepPercent(), img_y, new_y), interpolate(getStepPercent(), img_scalar, new_scalar), interpolate(getStepPercent(), img_angle, new_angle));
			} else if (step < begin) {
				Presentation.imgP(img_id, img_x, img_y, img_scalar, img_angle);
			} else {
				Presentation.imgP(img_id, new_x, new_y, new_scalar, new_angle);
			}
		},
		/* animated img */
		imgAE: function(img_id, img_x, img_y, new_x, new_y, img_width, img_height, new_width, new_height, img_angle, new_angle, begin, end) {
			if (step >= begin && step <= end) {
				Presentation.imgS(imd_id, interpolate(getStepPercent(), img_x, new_x), interpolate(getStepPercent(), img_y, new_y), interpolate(getStepPercent(), img_width, new_width), interpolate(getStepPercent(), img_height, new_height), interpolate(getStepPercent(), img_angle, new_angle));
			}
		},
		/* animated, perma img */
		imgPAE: function(img_id, img_x, new_y, new_x, img_y, img_scalar, new_scalar, img_angle, new_angle, begin, end) {
			if (step >= begin && step <= end) {
				Presentation.imgP(imd_id,interpolate(getStepPercent(), img_x, new_x), interpolate(getStepPercent(), img_y, new_y), interpolate(getStepPercent(), img_scalar, new_scalar), interpolate(getStepPercent(), img_angle, new_angle));
			}
		},
		/* draw a box */
		imgE: function(img_id, img_x, img_y, img_angle, img_appear, img_disappear) {
			if (step >= img_appear && step <= img_disappear) {
				Presentation.img(img_id, img_x, img_y, img_angle);
			}
		},
		/* draw a box */
		imgPE: function(img_id, img_x, img_y, img_scalar, img_angle, img_appear, img_disappear) {
			if (step >= img_appear && step <= img_disappear) {
				Presentation.imgP(img_id, img_x, img_y, img_scalar, img_angle);
			}
		},
		/* draw a box */
		imgSE: function(img_id, img_x, img_y, img_width, img_height, img_angle, img_appear, img_disappear) {
			if (step >= img_appear && step <= img_disappear) {
				Presentation.imgS(img_id, img_x, img_y, img_width, img_height, img_angle);
			}
		},

		

		/* video, true size */
		vid: function(vid_id, vid_x, vid_y, vid_angle, begin, end) {
			if (step >= begin && step <= end) {
				if (data_vid_running[vid_id] != true) {data_vid[vid_id].play(); data_vid_running[vid_id] = true;}
				var vid_width = data_vid[vid_id].videoWidth;
				var vid_height = data_vid[vid_id].videoHeight;
				if (clipping(vid_x, vid_y, lengthOf(vid_width, vid_height)/2) == true) {
					vid_y = vid_y-(vid_height/2);
					vid_x = vid_x-(vid_width/2);
					ctx.translate((vid_x+vid_width/2)*scalar, (vid_y+vid_height/2)*scalar);
					ctx.rotate((vid_angle)*Math.PI /180);
					ctx.translate(-(vid_x+vid_width/2)*scalar, -(vid_y+vid_height/2)*scalar);
					var xpos = vid_x*scalar;
					var ypos = vid_y*scalar;
					/* draw the scaled image */
					ctx.drawImage(data_vid[vid_id],xpos, ypos, vid_width*scalar, vid_height*scalar);
					ctx.translate((vid_x+vid_width/2)*scalar, (vid_y+vid_height/2)*scalar);
					ctx.rotate((-vid_angle)*Math.PI /180);
					ctx.translate(-(vid_x+vid_width/2)*scalar, -(vid_y+vid_height/2)*scalar);
				}
			} else {
				var vid_width = data_vid[vid_id].videoWidth;
				var vid_height = data_vid[vid_id].videoHeight;
				if (clipping(vid_x, vid_y, lengthOf(vid_width, vid_height)/2) == true) {
					vid_y = vid_y-(vid_height/2);
					vid_x = vid_x-(vid_width/2);
					ctx.translate((vid_x+vid_width/2)*scalar, (vid_y+vid_height/2)*scalar);
					ctx.rotate((vid_angle)*Math.PI /180);
					ctx.translate(-(vid_x+vid_width/2)*scalar, -(vid_y+vid_height/2)*scalar);
					var xpos = vid_x*scalar;
					var ypos = vid_y*scalar;
					/* draw the scaled image */
					ctx.drawImage(data_vid[vid_id],xpos, ypos, vid_width*scalar, vid_height*scalar);
					var temp = ctx.fillStyle;
					ctx.fillStyle = "rgba(0,0,0,0.5)";
					ctx.fillRect(xpos, ypos, vid_width*scalar, vid_height*scalar);
					ctx.fillStyle = temp;
					ctx.translate((vid_x+vid_width/2)*scalar, (vid_y+vid_height/2)*scalar);
					ctx.rotate((-vid_angle)*Math.PI /180);
					ctx.translate(-(vid_x+vid_width/2)*scalar, -(vid_y+vid_height/2)*scalar);
				}
				data_vid[vid_id].pause();
				data_vid_running[vid_id] = false;
			}
		},
		/* video true proportions */
		vidP: function(vid_id, vid_x, vid_y, vid_scalar, vid_angle, begin, end) {
			if (step >= begin && step <= end) {
				if (vid_id < 0) return;
				if (data_vid_running[vid_id] != true) {data_vid[vid_id].play(); data_vid_running[vid_id] = true;}
				var vid_width = data_vid[vid_id].videoWidth*vid_scalar;
				var vid_height = data_vid[vid_id].videoHeight*vid_scalar;
				if (clipping(vid_x, vid_y, lengthOf(vid_width, vid_height)/2) == true) {
					vid_y = vid_y-(vid_height/2);
					vid_x = vid_x-(vid_width/2);
					ctx.translate((vid_x+vid_width/2)*scalar, (vid_y+vid_height/2)*scalar);
					ctx.rotate((vid_angle)*Math.PI /180);
					ctx.translate(-(vid_x+vid_width/2)*scalar, -(vid_y+vid_height/2)*scalar);
					var xpos = vid_x*scalar;
					var ypos = vid_y*scalar;
					/* draw the scaled image */
					ctx.drawImage(data_vid[vid_id],xpos, ypos, vid_width*scalar, vid_height*scalar)
					ctx.translate((vid_x+vid_width/2)*scalar, (vid_y+vid_height/2)*scalar);
					ctx.rotate((-vid_angle)*Math.PI /180);
					ctx.translate(-(vid_x+vid_width/2)*scalar, -(vid_y+vid_height/2)*scalar);
				}
			} else {
				var vid_width = data_vid[vid_id].videoWidth*vid_scalar;
				var vid_height = data_vid[vid_id].videoHeight*vid_scalar;
				if (clipping(vid_x, vid_y, lengthOf(vid_width, vid_height)/2) == true) {
					vid_y = vid_y-(vid_height/2);
					vid_x = vid_x-(vid_width/2);
					ctx.translate((vid_x+vid_width/2)*scalar, (vid_y+vid_height/2)*scalar);
					ctx.rotate((vid_angle)*Math.PI /180);
					ctx.translate(-(vid_x+vid_width/2)*scalar, -(vid_y+vid_height/2)*scalar);
					var xpos = vid_x*scalar;
					var ypos = vid_y*scalar;
					/* draw the scaled image */
					ctx.drawImage(data_vid[vid_id],xpos, ypos, vid_width*scalar, vid_height*scalar);
					var temp = ctx.fillStyle;
					ctx.fillStyle = "rgba(0,0,0,0.5)";
					ctx.fillRect(xpos, ypos, vid_width*scalar, vid_height*scalar);
					ctx.fillStyle = temp;
					ctx.translate((vid_x+vid_width/2)*scalar, (vid_y+vid_height/2)*scalar);
					ctx.rotate((-vid_angle)*Math.PI /180);
					ctx.translate(-(vid_x+vid_width/2)*scalar, -(vid_y+vid_height/2)*scalar);
				}
				data_vid[vid_id].pause();
				data_vid_running[vid_id] = false;
			}
		},
		/* video, scaled */
		vidS: function(vid_id, vid_x, vid_y, vid_width, vid_height, vid_angle, begin, end) {
			if (step >= begin && step <= end) {
				if (data_vid_running[vid_id] != true) {data_vid[vid_id].play(); data_vid_running[vid_id] = true;}
				if (clipping(vid_x, vid_y, lengthOf(vid_width, vid_height)/2) == true) {
					vid_y = vid_y-(vid_height/2);
					vid_x = vid_x-(vid_width/2);
					ctx.translate((vid_x+vid_width/2)*scalar, (vid_y+vid_height/2)*scalar);
					ctx.rotate((vid_angle)*Math.PI /180);
					ctx.translate(-(vid_x+vid_width/2)*scalar, -(vid_y+vid_height/2)*scalar);
					var xpos = vid_x*scalar;
					var ypos = vid_y*scalar;
					/* draw the scaled image */
					ctx.drawImage(data_vid[vid_id],xpos, ypos, vid_width*scalar, vid_height*scalar);
					ctx.translate((vid_x+vid_width/2)*scalar, (vid_y+vid_height/2)*scalar);
					ctx.rotate((-vid_angle)*Math.PI /180);
					ctx.translate(-(vid_x+vid_width/2)*scalar, -(vid_y+vid_height/2)*scalar);
				}
			} else {
				if (clipping(vid_x, vid_y, lengthOf(vid_width, vid_height)/2) == true) {
					vid_y = vid_y-(vid_height/2);
					vid_x = vid_x-(vid_width/2);
					ctx.translate((vid_x+vid_width/2)*scalar, (vid_y+vid_height/2)*scalar);
					ctx.rotate((vid_angle)*Math.PI /180);
					ctx.translate(-(vid_x+vid_width/2)*scalar, -(vid_y+vid_height/2)*scalar);
					var xpos = vid_x*scalar;
					var ypos = vid_y*scalar;
					/* draw the scaled image */
					ctx.drawImage(data_vid[vid_id],xpos, ypos, vid_width*scalar, vid_height*scalar);
					var temp = ctx.fillStyle;
					ctx.fillStyle = "rgba(0,0,0,0.5)";
					ctx.fillRect(xpos, ypos, vid_width*scalar, vid_height*scalar);
					ctx.fillStyle = temp;
					ctx.translate((vid_x+vid_width/2)*scalar, (vid_y+vid_height/2)*scalar);
					ctx.rotate((-vid_angle)*Math.PI /180);
					ctx.translate(-(vid_x+vid_width/2)*scalar, -(vid_y+vid_height/2)*scalar);
				}
				data_vid[vid_id].pause();
				data_vid_running[vid_id] = false;
			}
		},
		/* video, true size */
		vidE: function(vid_id, vid_x, vid_y, vid_angle, begin, end) {
			if (step >= begin && step <= end) {
				Presentation.vid(vid_id, vid_x, vid_y, vid_angle, begin, end);
			} else {
				data_vid[vid_id].pause();
				data_vid_running[vid_id] = false;
			}
		},
		/* video true proportions */
		vidPE: function(vid_id, vid_x, vid_y, vid_scalar, vid_angle, begin, end) {
			if (step >= begin && step <= end) {
				Presentation.vid(vid_id, vid_x, vid_y, vid_scalar, vid_angle, begin, end);
			} else {
				data_vid[vid_id].pause();
				data_vid_running[vid_id] = false;
			}
		},
		/* video, scaled */
		vidSE: function(vid_id, vid_x, vid_y, vid_width, vid_height, vid_angle, begin, end) {
			if (step >= begin && step <= end) {
				Presentation.vid(vid_id, vid_x, vid_y, vid_width, vid_height, vid_angle, begin, end);
			} else {
				data_vid[vid_id].pause();
				data_vid_running[vid_id] = false;
			}
		},
		/* video, true size */
		vidA: function(vid_id, vid_x, vid_y, new_x, new_y, vid_angle, new_angle, begin, end) {
			if (step >= begin && step <= end) {
				Presentation.vid(vid_id, interpolate(getStepPercent(), vid_x, new_x), interpolate(getStepPercent(), vid_y, new_y), interpolate(getStepPercent(), vid_angle, new_angle), begin, end);
			} else if (step < begin) {
				Presentation.vid(vid_id, vid_x, vix_y, vid_angle, begin, end);
			} else {
				Presentation.vid(vid_id, new_x, new_y, new_angle, begin, end);
			}
		},
		/* video true proportions */
		vidPA: function(vid_id, vid_x, vid_y, new_x, new_y, vid_scalar, new_scalar, vid_angle, new_angle, begin, end) {
			if (step >= begin && step <= end) {
				Presentation.vidP(vid_id, interpolate(getStepPercent(), vid_x, new_x), interpolate(getStepPercent(), vid_y, new_y), interpolate(getStepPercent(), vid_scalar, new_scalar), interpolate(getStepPercent(), vid_angle, new_angle), begin, end);
			} else if (step < begin) {
				Presentation.vidP(vid_id, vid_x, vix_y, vid_scalar, vid_angle, begin, end);
			} else {
				Presentation.vidP(vid_id, new_x, new_y, new_scalar, new_angle, begin, end);
			}
		},
		/* video, scaled */
		vidSA: function(vid_id, vid_x, vid_y, new_x, new_y, vid_width, vid_height, new_width, new_height, vid_angle, new_angle, begin, end) {
			if (step >= begin && step <= end) {
				Presentation.vidP(vid_id, interpolate(getStepPercent(), vid_x, new_x), interpolate(getStepPercent(), vid_y, new_y), interpolate(getStepPercent(), vid_width, new_width), interpolate(getStepPercent(), vid_height, new_height), interpolate(getStepPercent(), vid_angle, new_angle), begin, end);
			} else if (step < begin) {
				Presentation.vidP(vid_id, vid_x, vix_y, vid_width, vid_height, vid_angle, begin, end);
			} else {
				Presentation.vidP(vid_id, new_x, new_y, new_width, new_height, new_angle, begin, end);
			}
		},
		/* video, true size */
		vidAE: function(vid_id, vid_x, vid_y, new_x, new_y, vid_angle, new_angle, begin, end) {
			if (step >= begin && step <= end) {
				Presentation.vid(vid_id, interpolate(getStepPercent(), vid_x, new_x), interpolate(getStepPercent(), vid_y, new_y), interpolate(getStepPercent(), vid_angle, new_angle), begin, end);
			} else {
				data_vid[vid_id].pause();
				data_vid_running[vid_id] = false;
			}
		},
		/* video true proportions */
		vidPAE: function(vid_id, vid_x, vid_y, new_x, new_y, vid_scalar, new_scalar, vid_angle, new_angle, begin, end) {
			if (step >= begin && step <= end) {
				Presentation.vidP(vid_id, interpolate(getStepPercent(), vid_x, new_x), interpolate(getStepPercent(), vid_y, new_y), interpolate(getStepPercent(), vid_scalar, new_scalar), interpolate(getStepPercent(), vid_angle, new_angle), begin, end);
			} else {
				data_vid[vid_id].pause();
				data_vid_running[vid_id] = false;
			}
		},
		/* video, scaled */
		vidSAE: function(vid_id, vid_x, vid_y, new_x, new_y, vid_width, vid_height, new_width, new_height, vid_angle, new_angle, begin, end) {
			if (step >= begin && step <= end) {
				Presentation.vidP(vid_id, interpolate(getStepPercent(), vid_x, new_x), interpolate(getStepPercent(), vid_y, new_y), interpolate(getStepPercent(), vid_width, new_width), interpolate(getStepPercent(), vid_height, new_height), interpolate(getStepPercent(), vid_angle, new_angle), begin, end);
			} else {
				data_vid[vid_id].pause();
				data_vid_running[vid_id] = false;
			}
		},

		/* audio */
		audio: function(audio_id, begin, end) {
			if (step >= begin && step <= end) {
				if (data_audio_running[audio_id] == false) {
					data_audio[audio_id].play();
					data_audio_running[audio_id] = true;
				}
			} else {
				data_audio[audio_id].pause();
				data_audio_running[audio_id] = false;
			}
		},
		/* custom function */
		func: function(func_func) {
			if (typeof (func_func) == 'function') {
				func_func(ctx, step, getStepPercent());
			}
		},
		/* custom functionE */
		funcE: function(func_func, begin, end) {
			if (step >= begin && step <= end && typeof (func_func) == 'function') {
				func_func(ctx, step, getStepPercent());
			}
		},
		/* hudmode, enable disable debug information */
		debug: function (b_debug) {
			debugmode = b_debug;
		},

		
		/* register image */
		loadImage: function(src) {
			var i = data_img.length;
			data_img[i] = new Image();   // Create new img element  
			data_img[i].onload = function(){  
  				data_img_loaded[i] = true;
			};  
			data_img[i].src = src; // Set source path
			return i;
		},
		/* register audio */
		loadAudio: function(src_name) {
			var i = data_audio.length;
			data_audio[i] = document.createElement("audio");  
			data_audio_running[i] = false;
			var srcs= document.createElement('source');
			if (data_audio[i].canPlayType('audio/mpeg;')) {
			    	srcs.type= 'audio/mpeg';
   		 	    	srcs.src= src_name+'.mp3';
			} else {
    				srcs.type= 'audio/ogg';
    				srcs.src= src_name+'.ogg';
			}
			data_audio[i].appendChild(srcs);
			return i;
		},
		/* register video */
		loadVideo: function(src_name) {
			var i = data_vid.length;
			data_vid[i] = document.createElement('video');  
			data_vid_running[i] = false;
			var srcs= document.createElement('source');
			if (data_vid[i].canPlayType('video/mpeg;')) {
			    	srcs.type= 'video/mpeg';
   		 	    	srcs.src= src_name+'.mp4';
			} else {
    				srcs.type= 'video/ogg';
    				srcs.src= src_name+'.ogg';
			}
			data_vid[i].appendChild(srcs);
			return i;
		},
		/* set the drawcolor */
		color: function(color) {			
			ctx.fillStyle = color;
			ctx.strokeStyle = color;
		},
		/* set the drawcolor */
		colorRGBA: function(red, green, blue, alpha) {
			if (alpha > 1) {
				alpha = alpha/255;
			}		
			ctx.fillStyle = "rgba("+red+","+green+","+blue+","+alpha+")";
			ctx.strokeStyle = "rgba("+red+","+green+","+blue+","+alpha+")";
		},
		/* set the drawcolor */
		colorRGB: function(red, green, blue) {		
			ctx.fillStyle = "rgb("+red+","+green+","+blue+")";
			ctx.strokeStyle = "rgb("+red+","+green+","+blue+")";
		},
		createLinearGradient: function(a, b, c, d) {
			if (d < 0)
				d = height;
			return ctx.createLinearGradient(a,b,c,d);
		},
		/* set the background */
		background: function(backgrd) {
			bkgDefault = false;
			bkgGrad = backgrd;
			if (bkgGrad == null) {
				bkgGrad = ctx.createLinearGradient(0, 0, 0, height);
				bkgGrad.addColorStop(0.5, '#000022');
				bkgGrad.addColorStop(0.8, '#000015');
				bkgGrad.addColorStop(0.9, '#000006');
				bkgGrad.addColorStop(1, '#000000');
				bkgDefault = true;
			}
			ctx.fillStyle = bkgGrad;
			ctx.fillRect(0, 0, width, height);
			
		}
	};
}
)();
